/******************************************************************************
 Copyright (c) 2022-2025 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #14 $
******************************************************************************/

#include "csr_synergy.h"

#ifndef EXCLUDE_GATT_XPAN_SERVER_MODULE

#include "gatt_xpan_server_common.h"
#include "gatt_xpan_server_db.h"

static void xpanServerFreeWiFiScanResultsIndContents(GattXpanWiFiScanResultsInd* msg)
{
    int i;
    for (i = 0 ; i < msg->numBssids; i++)
    {
        if(msg->apScanResult[i])
        {
            if (msg->apScanResult[i]->ssidLength > 0)
            {
                pfree(msg->apScanResult[i]->ssid);
                msg->apScanResult[i]->ssid = NULL;
            }
            pfree(msg->apScanResult[i]);
            msg->apScanResult[i] = NULL;
        }
        else
        {
            break;
        }
    }
}

void GattXpanServerFreeControlPointMessageContents(void *message)
{
    if(message)
    {
        GattXpanMessageId *id = (GattXpanMessageId *)message;

        switch(*id)
        {
            case GATT_XPAN_SERVER_CONNECT_SSID_IND:
            {
                GattXpanConnectSsidInd *msg = (GattXpanConnectSsidInd *)message;
                /* Passphrase, ssid, pmk*/
                pfree(msg->connectSsid.ssid);
                pfree(msg->passPhrase);
                pfree(msg->pmk);
                break;
            }
            case GATT_XPAN_SERVER_DISCONNECT_SSID_IND:
            {
                /* ssid */
                GattXpanDisconnectSsidInd *msg = (GattXpanDisconnectSsidInd *)message;
                pfree(msg->disconnectSsid.ssid);
                break;
            }
            case GATT_XPAN_SERVER_CONNECTED_SSID_IND:
            {
                /* Ssid, Passphrase, pmk */
                GattXpanConnectedSsidInd *msg = (GattXpanConnectedSsidInd *)message;

                pfree(msg->connectedSsid.ssid);
                pfree(msg->passphrase.value);
                pfree(msg->pmk);
                break;
            }
            case GATT_XPAN_SERVER_AVAILABLE_SSID_IND:
            {
                GattXpanAvailableSsidInd *msg = (GattXpanAvailableSsidInd *)message;

                pfree(msg->availableSsid.ssid);
                pfree(msg->passphrase);
                pfree(msg->bssidData);
                pfree(msg->pmk);
                break;
            }
            case GATT_XPAN_SERVER_RELATED_SSID_IND:
            {
                GattXpanRelatedSsidInd *msg = (GattXpanRelatedSsidInd *)message;
                uint8 i = 0;
                for(; i < msg->numSsids; i++)
                    pfree(msg->relatedSsid[i].ssid);
                pfree(msg->relatedSsid);
                break;
            }
            case GATT_XPAN_SERVER_REMOVE_SSID_IND:
            {
                GattXpanRemoveSsidInd *msg = (GattXpanRemoveSsidInd *)message;

                pfree(msg->removeSsid.ssid);
                break;
            }
            case GATT_XPAN_SERVER_WIFI_SCAN_RESULTS_IND:
            {
                xpanServerFreeWiFiScanResultsIndContents(message);
                break;
            }
            case GATT_XPAN_SERVER_AP_CONNECT_ASSIST_IND:
			{
                GattXpanApConnectAssistInd *msg = (GattXpanApConnectAssistInd *)message;

                pfree(msg->ssid);
                pfree(msg->ipv4Address);
                pfree(msg->macAddress);
                break;
            }
            case GATT_XPAN_SERVER_CONNECTED_ENTERPRISE_SSID_IND:
            {
                GattXpanConnectedEnterpriseSsidInd *msg = (GattXpanConnectedEnterpriseSsidInd *)message;
                /* Free ssid, identity, password */
                pfree(msg->connectedEnterpriseSsid.ssid);
                pfree(msg->identity.value);
                pfree(msg->password.value);
                break;
            }
            default:
                break;
        }
    }
}

static status_t xpanServerSetCCC(GattXpanServerData *gattXpanServerInstance,
                                ConnectionId cid,
                                uint16 handle,
                                uint8 *ccc,
                                bool* clientConfigChanged)
{
    XpanClientDataElement *client = xpanFindClient(&gattXpanServerInstance->data.connectedClients, cid);

    if(client)
    {
        switch (handle)
        {
            case HANDLE_STORED_SSID_CLIENT_CONFIG:
                if (client->clientData.clientCfg.storedSsidClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.storedSsidClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_IPV4_ADDRESSES_CLIENT_CONFIG:
                if (client->clientData.clientCfg.ipv4AddressClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.ipv4AddressClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_IPV6_ADDRESSES_CLIENT_CONFIG:
                if (client->clientData.clientCfg.ipv6AddressClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.ipv6AddressClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_L2CAP_TCP_PORT_CLIENT_CONFIG:
                if (client->clientData.clientCfg.l2capTcpPortClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.l2capTcpPortClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_UDP_PORT_CLIENT_CONFIG:
                if (client->clientData.clientCfg.udpPortClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.udpPortClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_CONNECTED_DEVICES_CLIENT_CONFIG:
                if (client->clientData.clientCfg.connectedDevicesClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.connectedDevicesClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_CLEAR_TO_SEND_CLIENT_CONFIG:
                if (client->clientData.clientCfg.clearToSendCfg != ccc[0])
                {
                    client->clientData.clientCfg.clearToSendCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_BEARER_PREFERENCE_CLIENT_CONFIG:
                if (client->clientData.clientCfg.bearerPreferenceClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.bearerPreferenceClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_MAC_ADDRESSES_CLIENT_CONFIG:
                if (client->clientData.clientCfg.macAddressesClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.macAddressesClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_REQUESTED_SAP_POWER_STATE_CLIENT_CONFIG:
                if (client->clientData.clientCfg.requestedSapPowerStateCfg != ccc[0])
                {
                    client->clientData.clientCfg.requestedSapPowerStateCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_AUDIO_BEARER_SWITCH_RESPONSE_CLIENT_CONFIG:
                if (client->clientData.clientCfg.audioBearerSwitchResponseCfg != ccc[0])
                {
                    client->clientData.clientCfg.audioBearerSwitchResponseCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_SERVER_FEATURES_CLIENT_CONFIG:
                if (client->clientData.clientCfg.serverFeaturesCfg != ccc[0])
                {
                    client->clientData.clientCfg.serverFeaturesCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_NUM_DEVICES_PRESENT_CLIENT_CONFIG:
                if (client->clientData.clientCfg.numDevicesPresentClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.numDevicesPresentClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_VOICE_BACK_CHANNEL_PERIODICITY_CLIENT_CONFIG:
                if (client->clientData.clientCfg.voiceBackChannelPeriodicityClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.voiceBackChannelPeriodicityClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_REQUEST_WIFI_SCAN_RESULTS_CLIENT_CONFIG:
                if (client->clientData.clientCfg.requestWiFiScanResultsClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.requestWiFiScanResultsClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_ROAMING_REQUEST_RESPONSE_CLIENT_CONFIG:
                if (client->clientData.clientCfg.roamingRequestResponseClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.roamingRequestResponseClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_WIFI_SCAN_RESULTS_CLIENT_CONFIG:
				if (client->clientData.clientCfg.wiFiScanResultsClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.wiFiScanResultsClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_PREFERRED_CHANNELS_CLIENT_CONFIG:
				if (client->clientData.clientCfg.preferredChannelsClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.preferredChannelsClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_WIFI_CHANNEL_SWITCH_REQUEST_CLIENT_CONFIG:
				if (client->clientData.clientCfg.wiFiChannelSwitchRequestClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.wiFiChannelSwitchRequestClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_SUPPORTED_WIFI_CHANNELS_CLIENT_CONFIG:
				if (client->clientData.clientCfg.supportedWiFiChannelsClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.supportedWiFiChannelsClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_TWT_STATUS_CLIENT_CONFIG:
                if (client->clientData.clientCfg.twtStatusClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.twtStatusClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            case HANDLE_SAP_CONNECTION_STATUS_CLIENT_CONFIG:
                if (client->clientData.clientCfg.sapConnectionStatusClientCfg != ccc[0])
                {
                    client->clientData.clientCfg.sapConnectionStatusClientCfg = ccc[0];
                    *clientConfigChanged = TRUE;
                }
                break;
            default:
                /* Invalid handle */
                GATT_XPAN_SERVER_ERROR("Invalid handle!\n");
                return CSR_BT_GATT_RESULT_INVALID_HANDLE_RANGE;
        }
    }
    else
    {
        /* Invalid cid */
        GATT_XPAN_SERVER_ERROR("Invalid cid!\n");
        return CSR_BT_GATT_RESULT_UNKNOWN_CONN_ID;
    }
    return CSR_BT_GATT_RESULT_SUCCESS;
}

static void xpanServerSendClientConfigChangedInd(GattXpanServerData *gattXpanServerInstance,
                                                        ConnectionId cid)
{
    GattXpanClientConfigChangedInd *message = (GattXpanClientConfigChangedInd *)CsrPmemZalloc(sizeof(GattXpanClientConfigChangedInd));
    message->cid = cid;
    XpanMessageSend(gattXpanServerInstance->appTask, GATT_XPAN_SERVER_CLIENT_CONFIG_CHANGED_IND, message);
}

void xpanServerSendAccessRsp(
        CsrBtGattId task,
        ConnectionId cid,
        uint16 handle,
        uint16 result,
        uint16 sizeValue,
        uint8 *const value)
{
    CsrBtGattDbReadAccessResSend(task,
                                 cid,
                                 handle,
                                 result,
                                 sizeValue,
                                 value);
}

void xpanServerWriteGenericResponse(
        CsrBtGattId task,
        ConnectionId cid,
        uint16      result,
        uint16      handle)
{
    if (task == CSR_BT_GATT_INVALID_GATT_ID)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: Invalid GattId!\n");
    }
    else if (cid == 0)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: No Cid\n");
    }
    else
    {
        CsrBtGattDbWriteAccessResSend(task,
                                      cid,
                                      handle,
                                      (CsrBtGattDbAccessRspCode)result);
    }
}

void xpanServerSendCharacteristicChangedNotification(
        CsrBtGattId  task,
        ConnectionId cid,
        uint16 handle,
        uint16 sizeValue,
        uint8 *const value)
{
    if (task == CSR_BT_GATT_INVALID_GATT_ID)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: Invalid GattId!\n");
    }
    else if (cid == 0)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: No Cid\n");
    }
    else
    {
        CsrBtGattNotificationEventReqSend(task,
                                          cid,
                                          handle,
                                          sizeValue,
                                          value);
    }
}

void xpanServerHandleReadClientConfigAccess(
        CsrBtGattId  task,
        ConnectionId cid,
        uint16 handle,
        const uint16 clientConfig)
{
    uint8 *configData = (uint8*) CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_CCC_VALUE_SIZE);

    if (task == CSR_BT_GATT_INVALID_GATT_ID)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: Invalid GattId!\n");
    }
    else if (cid == 0)
    {
        GATT_XPAN_SERVER_PANIC(
            "XPAN: No Cid\n");
    }

    CSR_COPY_UINT16_TO_LITTLE_ENDIAN(clientConfig, configData);

    xpanServerSendAccessRsp(
            task,
            cid,
            handle,
            CSR_BT_GATT_RESULT_SUCCESS,
            GATT_XPAN_SERVER_CCC_VALUE_SIZE,
            configData);
}

void xpanServerHandleWriteClientConfigAccess(
        GattXpanServerData *gattXpanServerInstance,
        CsrBtGattDbAccessWriteInd *const accessInd)
{
    uint8 i;
    uint16 sizeValue = 0;

    for(i = 0; i < accessInd->writeUnitCount; i++)
        sizeValue += accessInd->writeUnit[i].valueLength;

    if (sizeValue != GATT_XPAN_SERVER_CCC_VALUE_SIZE)
    {
        xpanServerSendAccessErrorRsp(
                    gattXpanServerInstance->gattId,
                    accessInd->btConnId,
                    accessInd->attrHandle,
                    CSR_BT_GATT_RESULT_INVALID_LENGTH);
    }
    else if (accessInd->writeUnit[0].value[0] == GATT_XPAN_SERVER_CCC_NOTIFY || accessInd->writeUnit[0].value[0] == 0)
    {
        /* Valid value of CCC */
        bool clientConfigChanged = FALSE;

        /* Save the new ccc in the library */
        status_t status = xpanServerSetCCC(
                                   gattXpanServerInstance,
                                   (connection_id_t) accessInd->btConnId,
                                   accessInd->attrHandle,
                                   accessInd->writeUnit[0].value,
                                   &clientConfigChanged);

        if (clientConfigChanged)
        {
            /* Inform application for client write operation */
            xpanServerSendClientConfigChangedInd(gattXpanServerInstance, accessInd->btConnId);
        }

        /* Send response to the client */
        xpanServerWriteGenericResponse(
                    gattXpanServerInstance->gattId,
                    accessInd->btConnId,
                    status,
                    accessInd->attrHandle);
    }
    else
    {
        /* Send response to the client but the value is ignored*/
        xpanServerWriteGenericResponse(
                    gattXpanServerInstance->gattId,
                    accessInd->btConnId,
                    CSR_BT_GATT_RESULT_SUCCESS,
                    accessInd->attrHandle);
    }
}

bool xpanCompareStoredSsids(GattXpanServerData *gattXpanServerInstance, const StoredSsid *storedSsid)
{
    uint8 i;

    /* Compare the number of stored ssids */
    if (gattXpanServerInstance->data.storedSsid.numSsid != storedSsid->numSsid)
    {
        return FALSE;
    }

    /* Iterate and compare each ssid */
    for (i = 0; i < storedSsid->numSsid; i++)
    {
        if (gattXpanServerInstance->data.storedSsid.ssid[i].ssidLength != storedSsid->ssid[i].ssidLength)
            return FALSE;

        if(storedSsid->ssid[i].ssidLength >= GATT_XPAN_SERVER_MAX_SSID_SIZE)
            return FALSE;

        if(CsrMemCmp(gattXpanServerInstance->data.storedSsid.ssid[i].ssid,
                    storedSsid->ssid[i].ssid,
                    storedSsid->ssid[i].ssidLength))
            return FALSE;
    }

    /* Everything is the same */
    return TRUE;
}

uint16 xpanServerStoredSsidSize(GattXpanServerData *gattXpanServerInstance)
{
    uint8 i;
    /* Size starts at 1 to accomodate numSsid */
    uint16 size = 1;

    for(i = 0; i < gattXpanServerInstance->data.storedSsid.numSsid; i++)
    {
        /* Increment size by length of ssid + 1 for length of ssid itself */
        size += gattXpanServerInstance->data.storedSsid.ssid[i].ssidLength + 1;
    }

    return size;
}

uint8* xpanServerComposeStoredSsidValue(uint16 *characteristicLength, GattXpanServerData *gattXpanServerInstance)
{
    uint16 j;
    uint8 *characteristicValue = NULL;
    GattBuffIterator iter;
    *characteristicLength = xpanServerStoredSsidSize(gattXpanServerInstance);

    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.storedSsid.numSsid);

    /* Iterate through Stored ssids */
    for(j = 0; j < gattXpanServerInstance->data.storedSsid.numSsid; j++)
    {
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.storedSsid.ssid[j].ssidLength);
        GattBuffIteratorWriteMultipleOctets(&iter, gattXpanServerInstance->data.storedSsid.ssid[j].ssid,
                                            gattXpanServerInstance->data.storedSsid.ssid[j].ssidLength);
    }

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

uint16 xpanServerGetIpv4AddressCharacteristicSize(const GattXpanServerData *gattXpanServerInstance)
{
    /*
     * QBCESPEC 4/4/2023
     *
     * Num SSIDs                1 Octet     Total number of SSIDs
       SSID Length[i]           1 Octet     The length of the SSID[i] field
       SSID[i]                  SSID Length[i] Octets
       BSSID[i]                 6 Octets    BSSID
       Num IPv4 Addresses[i]    1 Octet     Total number of IPv4 addresses
       Status[j]                1 Octet
       IP Address[j]            4 Octets    IPv4 Address
       Audio Location[j]        4 Octets    Audio Location associated with IP Address[j]
       Role[j]                  1 Octet     Role associated with IP Address[j]
       MAC Address[j]           6 Octets    MAC address associated with IP Address[j]
     */
    uint16 size = 1; /* num SSIDs */
    uint8 j;

    for(j = 0; j < gattXpanServerInstance->data.ipv4Address.numSsids; j++)
    {
        size += 1 + /* ssid length field*/
                gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Ssid.ssidLength + /* octets required for the SSID itself */
                + GATT_XPAN_SERVER_BSSID_SIZE
                + 1 /* num IPv4 Addresses field*/
                + ((1 + /* status */
                    GATT_XPAN_SERVER_IPV4_ADDRESS_SIZE + /* IPv4 Address */
                    4 + /* Audio Location */
                    1 + /* Role */
                    GATT_XPAN_SERVER_MAC_ADDRESS_SIZE) * gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].numIpv4Addresses);
    }
    return size;
}

uint16 xpanServerGetIpv6AddressCharacteristicSize(const GattXpanServerData *gattXpanServerInstance)
{
    /*
     * QBCESPEC 5/4/2023
     *
    Num SSIDs               1  Octet                Total number of SSIDs
    SSID Length[i]          1  Octet                The length of the SSID[i] field
    SSID[i]                 SSID Length[i] Octets
    BSSID[i]                6  Octets               BSSID
    Num IPv6 Addresses[i]   1  Octet                Total number of IPv6 addresses
    Status[j]               1  Octet
    IP Address[j]           16 Octets               IPv6 Address
    Audio Location[j]       4  Octets               Audio Location associated with IP Address[j]
    Role[j]                 1  Octet                Role associated with IP Address[j]
    MAC Address[j]          6  Octets               MAC address associated with IP Address[j]
    */

    uint16 size = 1; /* num SSIDs */
    uint8 j;

    for(j = 0; j < gattXpanServerInstance->data.ipv6Address.numSsids; j++)
    {
        size += 1 + /* ssid length field*/
                gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Ssid.ssidLength + /* octets required for the SSID itself */
                + GATT_XPAN_SERVER_BSSID_SIZE
                + 1 /* num IPv6 Addresses field*/
                + ((1 + /* status */
                    GATT_XPAN_SERVER_IPV6_ADDRESS_SIZE + /* IPv6 Address */
                    4 + /* Audio Location */
                    1 + /* Role */
                    GATT_XPAN_SERVER_MAC_ADDRESS_SIZE) * gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].numIpv6Addresses);
    }

    return size;
}

uint8* xpanServerComposeIpv4AddressValue(uint16 *characteristicLength, const GattXpanServerData *gattXpanServerInstance)
{
    uint8 j, k;
    GattBuffIterator iter;
    uint8 *characteristicValue = NULL;

    *characteristicLength = xpanServerGetIpv4AddressCharacteristicSize(gattXpanServerInstance);
    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    /* Structure of value:
     * value[0] -> numSsids
     * value[1] -> ssidLength[0]
     * value[2] - value[ssidLength+1] -> ssid[0]
     * value[ssidLength+1] - value[ssidLength+7] -> bssid
     * value[ssidLength[0]+7] -> numIpv4Addresses[0]
     * value[ssidLength[0]+8] - value[ssidLength+18] -> Ipv4Address[0]
     * value[ssidLength[0]+18] - value[ssidLength+28] -> Ipv4Address[1]
     * ...
     * value[ssidLength[0]+numIpv4Addresses[0]*10+8] -> ssidLength[1]
     * ...
     *
     */
    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv4Address.numSsids);

    for(j = 0; j < gattXpanServerInstance->data.ipv4Address.numSsids; j++)
    {
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Ssid.ssidLength);
        GattBuffIteratorWriteMultipleOctets(&iter,
                                            gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Ssid.ssid,
                                            gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Ssid.ssidLength);

        GattBuffIteratorWriteMultipleOctets(&iter,
                                            gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].bssid,
                                            GATT_XPAN_SERVER_BSSID_SIZE);
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].numIpv4Addresses);

        /* Copy list of Ipv4 addresses */
        for(k = 0; k < gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].numIpv4Addresses; k++)
        {
            GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Addresses[k].status);
            GattBuffIteratorWriteMultipleOctets(&iter,
                                                gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Addresses[k].ipv4Address,
                                                XPAN_SERVER_IPV4_ADDRESS_SIZE);
            GattBuffIteratorWrite32(&iter, gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Addresses[k].audioLocation);
            GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Addresses[k].role);
            GattBuffIteratorWriteMultipleOctets(&iter,
                                                gattXpanServerInstance->data.ipv4Address.ipv4Ssids[j].ipv4Addresses[k].macAddress,
                                                GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
        }
    }

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

uint8* xpanServerComposeIpv6AddressValue(uint16 *characteristicLength, const GattXpanServerData *gattXpanServerInstance)
{
    /* Structure of value:
     * value[0] -> numSsids
     * value[1] -> ssidLength[0]
     * value[2] - value[ssidLength+1] -> ssid[0]
     * value[ssidLength+1] - value[ssidLength+7] -> bssid
     * value[ssidLength[0]+7] -> numIpv6Addresses[0]
     * value[ssidLength[0]+8] - value[ssidLength+30] -> Ipv6Address[0]
     * value[ssidLength[0]+30] - value[ssidLength+55] -> Ipv6Address[1]
     * ...
     * value[ssidLength[0]+numIpv6Addresses[0]*24+8] -> ssidLength[1]
     * ...
     *
     */

    uint8 j, k;
    GattBuffIterator iter;
    uint8 *characteristicValue = NULL;

    *characteristicLength = xpanServerGetIpv6AddressCharacteristicSize(gattXpanServerInstance);
    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv6Address.numSsids);

    for(j = 0; j < gattXpanServerInstance->data.ipv6Address.numSsids; j++)
    {
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Ssid.ssidLength);
        GattBuffIteratorWriteMultipleOctets(&iter,
                                            gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Ssid.ssid,
                                            gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Ssid.ssidLength);

        GattBuffIteratorWriteMultipleOctets(&iter,
                                            gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].bssid,
                                            GATT_XPAN_SERVER_BSSID_SIZE);
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].numIpv6Addresses);

        /* Copy list of Ipv4 addresses */
        for(k = 0; k < gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].numIpv6Addresses; k++)
        {
            GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Addresses[k].status);
            GattBuffIteratorWriteMultipleOctets(&iter,
                                                gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Addresses[k].ipv6Address,
                                                XPAN_SERVER_IPV6_ADDRESS_SIZE);
            GattBuffIteratorWrite32(&iter, gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Addresses[k].audioLocation);
            GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Addresses[k].role);
            GattBuffIteratorWriteMultipleOctets(&iter,
                                                gattXpanServerInstance->data.ipv6Address.ipv6Ssids[j].ipv6Addresses[k].macAddress,
                                                GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
        }
    }

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

uint8* xpanServerComposeTwtConfigurationValue(uint16* characteristicLength, GattXpanServerData *gattXpanServerInstance, ConnectionId cid)
{
    GattBuffIterator iter;
    uint8* characteristicValue = NULL;
    XpanClientDataElement *client = xpanFindClient(&gattXpanServerInstance->data.connectedClients,
                                                     cid);
    *characteristicLength = 0;

    if(client != NULL)
    {
        *characteristicLength = sizeof(uint8)*GATT_XPAN_SERVER_TWT_CONFIGURATION_SIZE;
        characteristicValue = CsrPmemZalloc(*characteristicLength);

        GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

        /* Populate the characteristicValue */
        GattBuffIteratorWrite8(&iter, client->clientData.twtConfig.setupId);
        GattBuffIteratorWrite8(&iter, client->clientData.twtConfig.bitfield);
        GattBuffIteratorWrite32(&iter, client->clientData.twtConfig.desiredWakeDuration);
        GattBuffIteratorWrite32(&iter, client->clientData.twtConfig.desiredWakeInterval);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[0] >> 0x18) & 0x00FF);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[0] >> 0x10) & 0x00FF);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[0] >> 0x08) & 0x00FF);
        GattBuffIteratorWrite8(&iter, client->clientData.twtConfig.desiredWakeTsfLeft[0] & 0x00FF);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[1] >> 0x18) & 0x00FF);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[1] >> 0x10) & 0x00FF);
        GattBuffIteratorWrite8(&iter, (client->clientData.twtConfig.desiredWakeTsfLeft[1] >> 0x08) & 0x00FF);
        GattBuffIteratorWrite8(&iter, client->clientData.twtConfig.desiredWakeTsfLeft[1] & 0x00FF);
        GattBuffIteratorWrite32(&iter, client->clientData.twtConfig.desiredWakeTsfRightOffset);

        if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
        {
            pfree(characteristicValue);
            characteristicValue = NULL;
            *characteristicLength = 0;
        }
    }

    return characteristicValue;
}

uint8* xpanServerComposeConnectedDevicesValue(uint16* characteristicLength, GattXpanServerData *gattXpanServerInstance)
{
    uint8 j;
    GattBuffIterator iter;
    uint8* characteristicValue = NULL;

    *characteristicLength = (XPAN_SERVER_CONNECTED_DEVICES_DATA_SIZE*gattXpanServerInstance->data.connectedDevices.numDevices) + 1;
    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.connectedDevices.numDevices);

    for(j = 0; j < gattXpanServerInstance->data.connectedDevices.numDevices; j++)
    {
        GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.connectedDevices.connectedDevice[j].transport);
        GattBuffIteratorWrite16(&iter, gattXpanServerInstance->data.connectedDevices.connectedDevice[j].primaryFrequency);
    }

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

uint8* xpanServerComposeBearerPreferenceValue(uint16 *characteristicLength, GattXpanServerData *gattXpanServerInstance)
{
    GattBuffIterator iter;
    uint8* characteristicValue = NULL;

    *characteristicLength = sizeof(uint8)*GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE;
    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.bearerPreference.bearerPreference);
    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.bearerPreference.urgencyLevel);

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

uint8* xpanServerComposeMacAddressValue(uint16 *characteristicLength, GattXpanServerData *gattXpanServerInstance)
{
    uint8 j;
    GattBuffIterator iter;
    uint8* characteristicValue = NULL;

    *characteristicLength = 1 + (gattXpanServerInstance->data.macAddress.numMacAddresses * GATT_XPAN_SERVER_MAC_ADDRESS_T_SIZE);
    characteristicValue = CsrPmemZalloc(*characteristicLength);

    GattBuffIteratorInitialise(&iter, characteristicValue, *characteristicLength);

    GattBuffIteratorWrite8(&iter, gattXpanServerInstance->data.macAddress.numMacAddresses);

    for (j = 0; j < gattXpanServerInstance->data.macAddress.numMacAddresses; j++)
    {
        GattBuffIteratorWriteMultipleOctets(&iter, gattXpanServerInstance->data.macAddress.macAddressData[j].macAddress,
                                            GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
        GattBuffIteratorWrite32(&iter, gattXpanServerInstance->data.macAddress.macAddressData[j].audioLocation);
    }

    if(GATT_BUFF_ITERATOR_ERROR_DETECTED(&iter))
    {
        pfree(characteristicValue);
        characteristicValue = NULL;
        *characteristicLength = 0;
    }

    return characteristicValue;
}

XpanClientDataElement *xpanAddClient(CsrCmnList_t *connectedClients, ConnectionId cid)
{
    XpanClientDataElement *client = xpanFindClient(connectedClients, cid);

    if (client)
    {
        return client;
    }
    else
    {
        return (XpanClientDataElement *)CsrCmnListElementAddLast(connectedClients, sizeof(XpanClientDataElement));
    }
}

#endif /* EXCLUDE_GATT_XPAN_SERVER_MODULE */
